#/usr/bin/env bash

:<<description

功能：CICD
描述：
修改历史：
=======================================================
日期          版本号  修改人        修改内容

description

function usage(){
    echo "
    bash $0 <phrase>
    bash $0 help
    bash $0 pass
    bash $0 <phrase> <sequence>
    bash $0 <phrase> <sequence> <executeProfile>
    bash $0 <phrase> <sequence> <executeProfile> <ranges>
    "
}

#设置脚本异常立即退出
set -e
#获取当前脚本名（执行时调用的脚本路径）、脚本所在目录
script=$0
scriptBase=$(basename ${script})
scriptDir=$(cd $(dirname $0); pwd)
workDir=${scriptDir}
#声明环境变量，在声明时赋值不是必须的
for file in $(ls ${workDir}/env*.sh); do source ${file}; done
#导入通用函数
for file in $(ls ${workDir}/function*.sh); do source ${file}; done
#获取输入选项并赋值
#获取输入参数并命名
plans=$1
sequence=$2
executeProfile=$3
range=$4
#校验参数
if [[ ! "${plans}" ]]; then usage; logger "Positional parameter [plans] is not found" "error"; exit 1; fi
if [[ ! "${sequence}" ]]; then usage; logger "Positional parameter [sequence] is not found" "error"; exit 1; fi
if [[ ! "${executeProfile}" ]]; then usage; logger "Positional parameter [executeProfile] is not found" "error"; exit 1; fi

#其他参数
patternPlan='\s*(\S+)\s*:(.*)'
patternPlan='[[:space:]]*([^[:space:]]+)[[:space:]]*:(.*)'
patternIPv4='([0-9]{1,3}\.){3}[0-9]{1,3}'
patternIPv4WithUser='[a-zA-Z0-9_.+-]+@'${patternIPv4}
patternRange='^('${patternIPv4WithUser}'(,|$)){1,}$'
#校验其他参数

bash ${workDir}/generateNode.sh > /dev/null
bash ${workDir}/generatePlan.sh

#查询计划文件是否存在
function checkPlan () {
    #日志内容
    local plan=$1
    local planFile=${workDir}/plan/${plan}.lst
    for planSubDir in $(cd ${workDir}; ls plan/); do
        if [[ ! -d ${workDir}/plan/${planSubDir} ]]; then continue; fi
        if [[ -f ${workDir}/plan/${planSubDir}/${plan}.lst ]]; then
            planFile=${workDir}/plan/${planSubDir}/${plan}.lst
        fi
    done
    if [[ ! -f ${planFile} ]]; then echo -n ""; else echo -n ${planFile}; fi
}


function execute () {
    local action=$1
    local targetResource=$2
    local sequence=$3
    local executeProfile=$4
    local range=$5
    #local actionResource=${action}$(echo -n $resource | awk '{print toupper(substr($0,1,1))substr($0,2)}')
    local actionResource=${action}${targetResource^}
    local called

    if [ -f ${workDir}/node/actionResource/${actionResource}.sh ]; then
        called=${workDir}/node/actionResource/${actionResource}.sh
    elif [ -f ${workDir}/node/action/${action}.sh ]; then
        called=${workDir}/node/action/${action}.sh
    elif [ -f ${workDir}/node/action/link/${action}.link ]; then
        called=${workDir}/node/action/$(cat ${workDir}/node/action/link/${action}.link)
    elif [ -f ${workDir}/node/resource/${targetResource}.sh ]; then
        called=${workDir}/node/resource/${targetResource}.sh
    elif [ -f ${workDir}/node/resource/link/${targetResource}.link ]; then
        called=${workDir}/node/resource/$(cat ${workDir}/node/resource/link/${targetResource}.link)
    else
        logger "Unknown targetResource.sh/link or action.sh/link or resource.sh/link" "error"
        return 1
    fi

    #在本地执行
    if (( $(echo ${action} | sed -n -E '/^ssh/p' | wc -l) == 0 )); then

        logger "${barRightArrow30}"
        logger "action: ${action}"
        logger "targetResource: ${targetResource}"
        logger "sequence: ${sequence}"
        logger "executeProfile: ${executeProfile}"
        logger "${barRightArrow30}\n"

        bash ${called} ${action} ${targetResource} ${sequence} ${executeProfile}

        logger "${barLeftArrow30}"
        logger "Successfully ${action} ${targetResource} in ${executeProfile} identified by ${sequence}."
        logger "${barLeftArrow30}\n"

        return 0
    fi

    #远程执行
    rangeFile=${workDir}/range/${executeProfile}/${targetResource}.lst
    if [ ! -z ${range} ]; then
        if echo "${range}" | grep -E "${patternRange}"
            then range=${range//,/' '}
            else logger "Unknown range."; return 1
        fi

    elif [ ! -f ${rangeFile} ]; then
        logger "Range for ${targetResource} is not found." "error"
        return 1
    else
        range=$(cat ${rangeFile} | sed -n -E '/^$/!p' | sed -n -E '/^#/!p')
    fi
    for item in ${range}; do

        host=`echo -n ${item} | cut -d '@' -f 2`
        user=`echo -n ${item} | cut -d '@' -f 1`
        logger "${barRightArrow30}"
        logger "action: ${action}"
        logger "targetResource: ${targetResource}"
        logger "sequence: ${sequence}"
        logger "executeProfile: ${executeProfile}"
        logger "host: ${host}"
        logger "user: ${user}"
        logger "${barRightArrow30}\n"

        bash ${called} ${action} ${targetResource} ${sequence} ${executeProfile} ${host} ${user}

        logger "${barLeftArrow30}"
        logger "Successfully ${action} ${targetResource} with ${host}/${user} in ${executeProfile} identified by ${sequence}."
        logger "${barLeftArrow30}\n"
    done


}

#遍历计划
for plan in ${plans//,/' '}; do
    #每一个计划名对应一份计划文件
    planFile=$(checkPlan ${plan})
    if [[ -z ${planFile} ]]; then logger "Plan file for plan [${plan}]  is not found."; exit 1; fi
    #按行读取计划文件
    mapfile planItems < ${planFile}
    for (( i=0; i < ${#planItems[@]}; i++ )); do
        #解析每一行计划
        planItem=${planItems[${i}]}
        #解析跳过空行
        if (( `echo ${planItem} | sed -n -E '/^$/p' | wc -l` == 1  )); then continue; fi #空行
        #解析跳过注释
        if (( `echo ${planItem} | sed -n -E '/^#/p' | wc -l` == 1  )); then continue; fi #注释
        logger "${barEqual30}"
        logger "${planItem}"
        logger "${barEqual30}"
        #计划项当前格式“动作: 应用列表（空格分隔）”
        #计划项可选格式“应用：动作列表（空格分隔）”
        action=$(echo "${planItem}" | sed -n -E 's/'${patternPlan}'/\1/Ip')
        resources=$(echo "${planItem}" | sed -n -E 's/'${patternPlan}'/\2/Ip')
        #遍历资源对象
        for targetResource in ${resources}; do
            case ${action} in
                'executePlan')
                    logger "bash ${workDir}/${scriptBase} ${targetResource} ${sequence} ${executeProfile}"
                    execute ${workDir}/${scriptBase} ${targetResource} ${sequence} ${executeProfile}
                    continue
                    ;;
                'pass')
                    logger "Pass"
                    continue
                    ;;
                *) execute ${action} ${targetResource} ${sequence} ${executeProfile} ${range}; ;;
            esac
        #遍历资源对象
        done
    #按行读取计划文件
    done
    logger "Successfully ${plan} is executed." "info"
#遍历计划
done
logger "Succeed." "info"


